package com.dongbao.core.rules.java;

import com.dongbao.core.base.CodeRulesCommonBase;
import com.dongbao.core.rules.CodeRulesBase;
import com.dongbao.data.Datacenter;
import com.dongbao.dialog.DHGToolWindow;
import com.dongbao.entity.AwardedInfo;
import com.dongbao.util.StringArrayUtil;
import com.intellij.lang.jvm.JvmParameter;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.search.PsiAnnotationMethodReferencesSearcher;
import com.intellij.psi.impl.source.PsiClassImpl;
import com.intellij.psi.impl.source.PsiMethodImpl;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.AnnotationTargetsSearch;
import com.intellij.util.Query;

import java.util.*;
import java.util.stream.Collectors;

/**
 *   在一个事务中不能同时对同一张表做查询新增修改 删除，只要存在两种的就算，只提示，不扣分
 * */
public class LockTableRules extends CodeRulesCommonBase implements CodeRulesBase {

    private static final Logger LOGGER = Logger.getInstance(LockTableRules.class);

    private Map<String,List<PsiElement>> existOption=new HashMap<String,List<PsiElement>>();

    private Map<String,Map<String,List<PsiElement>>> serviceOpMap=new HashMap<>();

    private final List<String> queryKey=Arrays.asList("select","getBy","list");
    private final List<String> deleteKey=Arrays.asList("delete","remove");
    private final List<String> updateKey=Arrays.asList("update");
    private final List<String> saveKey=Arrays.asList("save","insert");

    private final String QUERY_KEY = "query";
    private final String UPDATE_KEY = "update";
    private final String DELETE_KEY = "delete";
    private final String SAVE_KEY = "save";

    public LockTableRules(VirtualFile afterFile,PsiFile psiFile,Project project) {
        super(afterFile,psiFile,project);
        this.code = "B010";
        this.level = "B";
    }

    /**
     * 执行规则判断
     * */
    @Override
    public void run(List<AwardedInfo> deduction) {
        existOption.clear();
        serviceOpMap.clear();
        /* 执行分析当前class是否负责当前规则 */
        PsiClass annoPsiClass = JavaPsiFacade.getInstance(this.project).findClass("org.springframework.transaction.annotation.Transactional", GlobalSearchScope.allScope(this.project));
        for(PsiClassImpl psiClass:psiClassImpls){
            /* 获取所有方法 */
            PsiMethod[] allMethods = psiClass.getMethods();
            if(allMethods == null){
                continue;
            }
            /* 搜索方法中是否存在使用的 @Transactional 注解的方法 */
            GlobalSearchScope globalSearchScope = GlobalSearchScope.fileScope(this.project,afterFile);
            Query<PsiModifierListOwner> search = AnnotationTargetsSearch.search(annoPsiClass, globalSearchScope);

            for(PsiModifierListOwner modifierListOwner:search){
                if(modifierListOwner instanceof PsiMethodImpl){
                    PsiMethodImpl method=(PsiMethodImpl)modifierListOwner;
                    PsiElement[] childrens = method.getBody().getChildren();
                    for(PsiElement children:childrens){
                        checkChildren(children);
                    }
                    String methodBody = method.getBody().getText();
                    /* 分析操作 归类操作的service  */
                    existOption.keySet();
                    for(String opKey:existOption.keySet()){
                        List<PsiElement> psiElements = existOption.get(opKey);
                        for(PsiElement element:psiElements){
                            if(element instanceof PsiDeclarationStatement){
                                //声明变量的方式
                                PsiDeclarationStatement statement=(PsiDeclarationStatement)element;
                                String serviceVariableName = statement.getChildren()[0].getChildren()[7].getFirstChild().getFirstChild().getText();
                                setService(serviceVariableName,opKey,statement);
                                LOGGER.info("6666");
                            }
                            if(element instanceof PsiExpressionStatement){
                                //直接调用表达式的方式
                                PsiExpressionStatement statement=(PsiExpressionStatement)element;
                                String serviceVariableName = statement.getFirstChild().getFirstChild().getFirstChild().getText();
                                if(StringUtil.isEmpty(serviceVariableName)){
                                    try{
                                        serviceVariableName = statement.getChildren()[0].getChildren()[4].getChildren()[0].getChildren()[0].getText();
                                        LOGGER.info("6666");
                                    }catch (Exception e){
                                        serviceVariableName = statement.getChildren()[0].getChildren()[0].getText();
                                        try{
                                            statement.getChildren()[0].getChildren()[0].getText();
                                        }catch (Exception ex){
                                            e.printStackTrace();
                                        }
                                    }
                                }
                                setService(serviceVariableName,opKey,statement);
                                LOGGER.info("6666");
                            }
                        }
                    }
                    /* 判断是否存在同一个业务类调用两种不同类型的情况 */
                    for(String serviceKey:serviceOpMap.keySet()){
                        if(serviceOpMap.get(serviceKey).size()>1){
                            Map<String, List<PsiElement>> opMap = serviceOpMap.get(serviceKey);
                            List<String> result = new ArrayList<>(opMap.keySet()) ;
                            List<PsiElement> errorElement = new ArrayList<PsiElement>();
                            for(String opKey:opMap.keySet()){
                                errorElement.addAll(opMap.get(opKey));
                            }

                            Document document = PsiDocumentManager.getInstance(psiClass.getProject()).getDocument(psiClass.getContainingFile());
                            int lineNumber = document.getLineNumber(errorElement.get(errorElement.size()-1).getTextOffset());
                            /* 没有写注释 */
                            String expTexts = errorElement.stream().map(PsiElement::getText).collect(Collectors.joining(","));
                            packDeduction(
                                    deduction,
                                    psiClass.getName(),
                                    psiPackageStatements.get(0).getPackageName(),
                                    method.getName(),
                                    "在一个事务中不能同时对同一张表做查询新增修改 删除。当前【"+serviceKey+"】存在【"+result.stream().collect(Collectors.joining(","))+"】的不同操作，表达式【"+expTexts+"】",lineNumber,lineNumber);

                        }



//                        for(String opKey:serviceOpMap.get(serviceKey).keySet()){
//                            List<PsiElement> psiElements = serviceOpMap.get(serviceKey).get(opKey);
//                            if(psiElements.size()>1){
//                                Document document = PsiDocumentManager.getInstance(psiClass.getProject()).getDocument(psiClass.getContainingFile());
//                                int lineNumber = document.getLineNumber(psiElements.get(psiElements.size()-1).getTextOffset());
//                                /* 没有写注释 */
//                                String expTexts = psiElements.stream().map(PsiElement::getText).collect(Collectors.joining(","));
//                                packDeduction(
//                                        deduction,
//                                        psiClass.getName(),
//                                        psiPackageStatements.get(0).getPackageName(),
//                                        method.getName(),
//                                        "在一个事务中不能同时对同一张表做查询新增修改 删除。当前【"+serviceKey+"】存在【"+opKey+"】的操作两次及以上，表达式【"+expTexts+"】",lineNumber,lineNumber);
//                            }
//                        }
                    }
                    LOGGER.info("6666");

                }

            }
            LOGGER.info("6666");
        }
    }

    public void setService(String serviceVariableName,String opKey,PsiElement statement){
        Map<String, List<PsiElement>> opTypeMap;
        if(!serviceOpMap.containsKey(serviceVariableName)){
            opTypeMap = new HashMap<String, List<PsiElement>>();
        }else{
            opTypeMap = serviceOpMap.get(serviceVariableName);
        }
        List<PsiElement> psiElements ;
        if(!opTypeMap.containsKey(opKey)){
            psiElements = new ArrayList<PsiElement>();
        }else{
            psiElements = opTypeMap.get(opKey);
        }
        psiElements.add(statement);
        opTypeMap.put(opKey,psiElements);
        serviceOpMap.put(serviceVariableName,opTypeMap);
    }

    public void checkChildren(PsiElement children){
        if(children instanceof PsiDocComment || children instanceof PsiWhiteSpace){
            /* 对于括号 注释 空格 回车的标签不进行处理 */
            return ;
        }
        if(children instanceof PsiDeclarationStatement || children instanceof PsiExpressionStatement){
            //声明变量 || 调用方法表达式
            //判断操作中是否存在调用了数据库的操作
            final String codeContext = children.getText();
            if(queryKey.stream().filter(item->{return codeContext.contains(item);}).collect(Collectors.toList()).size()>0){
                setExistOption(children,QUERY_KEY);
            }
            if(deleteKey.stream().filter(item->{return codeContext.contains(item);}).collect(Collectors.toList()).size()>0){
                setExistOption(children,DELETE_KEY);
            }
            if(updateKey.stream().filter(item->{return codeContext.contains(item);}).collect(Collectors.toList()).size()>0){
                setExistOption(children,UPDATE_KEY);
            }
            if(saveKey.stream().filter(item->{return codeContext.contains(item);}).collect(Collectors.toList()).size()>0){
                setExistOption(children,SAVE_KEY);
            }
            return ;
        }
        if(children.getChildren().length>0){
            for(PsiElement childrenElement:children.getChildren()){
                checkChildren(childrenElement);
            }
        }
    }

    public void setExistOption(PsiElement element,String key){
        List<PsiElement> elements;
        if(!existOption.containsKey(key)){
            elements = new ArrayList<PsiElement>();
        }else{
            elements = existOption.get(key);
        }
        elements.add(element);
        existOption.put(key,elements);
    }

    @Override
    public String describe() {
        return "数据库锁表";
    }

}
